/*
 * Copyright (C) 2018 Zhejiang xiaominfo Technology CO.,LTD.
 * All rights reserved.
 * Official Web Site: http://www.xiaominfo.com.
 * Developer Web Site: http://open.xiaominfo.com.
 */

package com.hank.swagger.service.doc.config;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Maps;
import com.hank.swagger.service.doc.entity.SwaggerResources;
import com.hank.swagger.service.doc.entity.eurekaapps.Application;
import com.hank.swagger.service.doc.entity.eurekaapps.EurekaApps;
import com.hank.swagger.service.doc.entity.eurekaapps.Instance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;

import javax.annotation.PostConstruct;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/***
 *
 * @author hank
 */
@Component
@Primary
@RequestMapping("/")
public class SwaggerResourceConfig implements SwaggerResourcesProvider {
    public static final String ROUTE_BASE_PATH = "/";

    @Value("${eureka.client.serviceUrl.defaultZone}")
    private String eurekaServiceUrls;

    @Value("${spring.application.name}")
    private String applicationName;

    @Autowired
    private AppApiConfig appApiConfig;


    private RestTemplate restTemplate = new RestTemplate();

    private final String apiDocsUri = "/apiDocsUri/";

    private Map<String, SwaggerResources> SwaggerResourceInfoMap;

    private Map<String, List<String>> appNameApiDocsIndexMap;

    Logger logger = LoggerFactory.getLogger(SwaggerResourceConfig.class);

    private List<SwaggerResource> swaggerResourceList;


    @Override
    public List<SwaggerResource> get() {
        return swaggerResourceList;
    }

    /**
     * 获取apiDoc
     *
     * @param apiDocsUriIndex
     * @return
     */
    @GetMapping(value = "apiDocsUri/{apiDocsUriIndex}", produces = {"application/json"})
    @ResponseBody
    public String getApiDocs(@PathVariable(value = "apiDocsUriIndex") String apiDocsUriIndex) {
        // 首页
        if (apiDocsUriIndex.equals("index")) {
            return appApiConfig.getApiIndexDoc();
        }
        SwaggerResources swaggerResources = SwaggerResourceInfoMap.get(apiDocsUriIndex);
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(swaggerResources.getVipFullAddress() + swaggerResources.getLocation(), String.class);
        return responseEntity.getBody();
    }

    /**
     * 刷新
     *
     * @return
     */
    @GetMapping("apiDocs/refresh")
    @ResponseBody
    public String apiDocsRefresh() {
        initEurekaApps();
        return "刷新成功";
    }

    /**
     * 初始化
     * 每天一点重新刷新
     */
    @PostConstruct
    @Scheduled(cron = "0 00 01 * * ? ")
    public void initEurekaApps() {
        try {
            StringBuilder requestUrl = new StringBuilder();
            requestUrl.append(eurekaServiceUrls.split(",")[0]);
            if (!StrUtil.endWith(requestUrl, ROUTE_BASE_PATH)) {
                requestUrl.append(ROUTE_BASE_PATH);
            }
            requestUrl.append("apps");
            String eurekaMetaApi = requestUrl.toString();
            logger.info("Eureka meta api:{}", eurekaMetaApi);
            // 去eureka服务注册信息
            HttpResponse httpResponse = HttpRequest.get(eurekaMetaApi)
                    .header("Accept", "application/json")
                    .execute();

            int statusCode = httpResponse.getStatus();
            logger.info("Eureka Response code:{}", statusCode);
            if (statusCode == HttpStatus.HTTP_OK) {
                String content = httpResponse.body();
                if (StrUtil.isNotBlank(content)) {
                    logger.info("Eureka Response body:{}", content);
                    EurekaApps eurekaApps = JSONUtil.toBean(content, EurekaApps.class);
                    SwaggerResourceInfoMap = new LinkedHashMap<>((int) (eurekaApps.getApplications().getApplication().size() * 2));
                    appNameApiDocsIndexMap = Maps.newHashMapWithExpectedSize(eurekaApps.getApplications().getApplication().size());
                    int apiDocsUriIndex = 1;
                    // 遍历服务
                    for (Application application : eurekaApps.getApplications().getApplication()) {
                        if (application != null) {
                            // 判断是不是当前服务
                            if (applicationName.equalsIgnoreCase(application.getName())) {
                                continue;
                            }
                            // 没有配置的不需要
                            if (!appApiConfig.getAppNameMap().containsKey(application.getName().toLowerCase())) {
                                continue;
                            }
                            List<Instance> instance = application.getInstance();
                            if (!CollectionUtils.isEmpty(instance)) {
                                Instance instanceNow = instance.get(0);
                                String vipFullAddress = instanceNow.getHomePageUrl();
                                List<SwaggerResources> swaggerResources = getSwaggerResources(vipFullAddress);
                                // 写入swaggerResources
                                if (!CollectionUtils.isEmpty(swaggerResources)) {
                                    for (SwaggerResources swaggerResource : swaggerResources) {
                                        String apiName = getApiName(application.getName(), swaggerResource.getName());
                                        swaggerResource.setVipFullAddress(vipFullAddress);
                                        swaggerResource.setName(apiName);
                                        SwaggerResourceInfoMap.put(String.valueOf(apiDocsUriIndex), swaggerResource);
                                        appNameApiDocsIndexMap.computeIfAbsent(application.getName().toLowerCase(), (key) -> new LinkedList<>())
                                                .add(String.valueOf(apiDocsUriIndex));
                                        apiDocsUriIndex++;
                                    }

                                }
                            }
                        }
                    }
                }
            }
            initSwaggerResourceList();
        } catch (Exception e) {
            logger.error("initEurekaApps error", e);
        }
    }


    private void initSwaggerResourceList() {
        // api资源列表
        swaggerResourceList = new LinkedList<>();
        if (StrUtil.isNotBlank(appApiConfig.getApiIndexDoc())) {
            // 设置一个默认的
            SwaggerResource indexSwaggerRes = new SwaggerResource();
            indexSwaggerRes.setName("首页");
            indexSwaggerRes.setUrl(apiDocsUri + "index");
            indexSwaggerRes.setSwaggerVersion("2.0");
            indexSwaggerRes.setLocation(apiDocsUri + "index");
            swaggerResourceList.add(indexSwaggerRes);
        }

        logger.info("SwaggerResourceInfoMap {}", SwaggerResourceInfoMap);
        logger.info("appNameMap {}", appApiConfig.getAppNameMap());
        if (MapUtil.isNotEmpty(SwaggerResourceInfoMap) && MapUtil.isNotEmpty(appApiConfig.getAppNameMap())) {
            for (String appName : appApiConfig.getAppNameMap().keySet()) {
                List<String> apiDocIndexList = appNameApiDocsIndexMap.get(appName);
                if (!CollectionUtils.isEmpty(apiDocIndexList)) {
                    for (String apiDocIndex : apiDocIndexList) {
                        SwaggerResources swaggerResources = SwaggerResourceInfoMap.get(apiDocIndex);
                        swaggerResourceList.add(swaggerResource(apiDocIndex, swaggerResources));
                    }
                }

            }
        }
    }

    /**
     * 转换成当前的获取apiDoc的地址
     *
     * @param apiDocsUriIndex
     * @param swaggerResources
     * @return
     */
    private SwaggerResource swaggerResource(String apiDocsUriIndex, SwaggerResources swaggerResources) {
        logger.info("swaggerResources:{}", swaggerResources);
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(swaggerResources.getName());
        swaggerResource.setUrl(apiDocsUri + apiDocsUriIndex);
        swaggerResource.setLocation(apiDocsUri + apiDocsUriIndex);
        swaggerResource.setSwaggerVersion(swaggerResources.getSwaggerVersion());

        return swaggerResource;
    }

    /**
     * 没有时返回null
     *
     * @param baseUrl
     * @return
     */
    private List<SwaggerResources> getSwaggerResources(String baseUrl) {

        try {
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(baseUrl + "/swagger-resources", String.class);
            if (responseEntity.getStatusCodeValue() != HttpStatus.HTTP_OK || StrUtil.isBlank(responseEntity.getBody())) {
                return null;
            }
            return JSONUtil.toList(responseEntity.getBody(), SwaggerResources.class);
        } catch (Exception e) {
            logger.error("getSwaggerResources e", e);
            return null;
        }
    }

    /**
     * 获得api名字
     *
     * @param appName
     * @param swaggerResourceName
     * @return
     */
    private String getApiName(String appName, String swaggerResourceName) {
        String newAppName = appApiConfig.getAppNameMap().get(appName.toLowerCase());
        if (StrUtil.isEmpty(newAppName)) {
            newAppName = appName;
        }
        if (swaggerResourceName.equals("default")) {
            return newAppName;
        } else {
            return (!StrUtil.isBlank(newAppName) ? (newAppName + "-") : "") + swaggerResourceName;
        }
    }
}
